import {PluginCommand} from "../../../Command/PluginCommand";
import {eventBus} from "../../../eventbus/eventbus";
import {PluginCADToolFunctionType} from "../../../../nodeApp/static/lib/crowncad-plugin-sdk-0.0.1";
import {ElementType, EntityType} from "../../../modeling/TypeDefine";
import {Vector3} from "three";

class TrussDesignCommand extends PluginCommand {
  constructor() {
    super();
    this.extrudeLength = 5000; // 按照5米拉伸切面
    this.sizeParam = null; // 尺寸参数
    this.selectedParam = null; // 选择参数
    this.type = "typeT"; // 当前选择类型
    this.from = 0; // 页面进入类型，0表示标题进入，1表示上一步进入
    this.names = {}; // 需要转换的名称
  }

  /**
   * 内核服务接口
   * @param serviceName 服务名
   * @param needDetail  是否需要明细
   * @param featureName 自定义特征名
   * @param params      参数
   * @returns {Promise<{elements: *[], name: *, entityId: number, id: *, error: number}>}
   */
  async getIncrementServiceByName(serviceName, needDetail, featureName, params) {
    let ret = null;
    let elements = [];
    let entityId = 0;
    // 执行内核接口调用
    switch (serviceName) {
      case "extrudeSurface":
        ret = await this.command.surface.extrudeSurfaceIncrement(params, "");
        break;
      case "copySurface":
        ret = await this.command.surface.copySurfaceIncrement(params, "");
        break;
      case "offsetSurface":
        ret = await this.command.surface.offsetSurfaceIncrement(params, "");
        break;
      case "loftSurface":
        ret = await this.command.surface.loftSurfaceIncrement(params, "");
        break;
      case "knitSurface":
        ret = await this.command.surface.knitSurfaceIncrement(params, "");
        break;
      case "createCutSplit":
        ret = await this.command.solid.createCutSplitIncrement(params, "");
        break;
      case "cutSplit":
        ret = await this.command.solid.cutSplitIncrement(params, "");
        break;
      case "thicken":
        ret = await this.command.solid.thickenIncrement(params, featureName ?? "");
        break;
      case "booleanSubtract":
        ret = await this.command.solid.booleanSubtractIncrement(params, featureName);
        break;
      case "setEntityVisible":
        ret = await this.command.common.setEntityVisibleIncrement(params);
        break;
    }
    // 执行失败返回错误
    if (ret.code !== 200) {
      throw "netError";
    }
    // 如果无需明细数据则直接返回，主要针对 接口setEntityVisible
    if (!needDetail) {
      return {};
    }
    // 其它接口特征操作失败
    if (ret.data?.modelData?.error || !ret.data?.modelData?.currentFeature) {
      throw serviceName;
    }
    // 获取明细数据
    let [feature] = ret.data.modelData.currentFeature;
    let eleData = ret.data?.modelData?.data?.addElements;
    if (eleData && eleData.length > 0) {
      // 调用cc端界面查询
      let {
        allElements,
        defaultEntityId
      } = await this.getQueryServiceByType("getElementsByIds", [...eleData.map(j => j.id)], [])
      elements = allElements;
      entityId = defaultEntityId;
    }
    return {
      error: 0,
      id: feature?.id,
      name: feature?.name,
      elements,
      entityId
    }
  }

  /**
   * cc端界面元素查询
   * @param typeName    查询方法名
   * @param params      查询参数
   * @param returnType  元素类型过滤
   * @returns {Promise<{defaultEntityId: *[], allElements: *[]}>}
   */
  async getQueryServiceByType(typeName, params, returnType) {
    returnType = returnType ?? [];
    let serviceRet = null;
    let ret = {
      allElements: [],
      defaultEntityId: [],
      entities: []
    }
    switch (typeName) {
      case "getElementsByEntityId":
        serviceRet = await this.query.getElementsByEntityId(params)
        break;
      case "getElementsByIds":
        serviceRet = await this.query.getElementsByIds(params);
        break;
      case "getElementsByFeatureNames":
        serviceRet = await this.query.getElementsByFeatureNames(params)
        break;
      case "getElementsByFeatureId":
        serviceRet = await this.query.getElementsByFeatureId(params)
        break;
      case "getEntitiesByFeatureIds":
        serviceRet = await this.query.getEntitiesByFeatureIds(params)
        break;
      case "getEntitiesByFeatureNames":
        serviceRet = await this.query.getEntitiesByFeatureNames(params)
        break;
    }
    if (serviceRet === null || serviceRet.code !== 200 || !serviceRet.data) {
      throw "netError"
    }
    // 解析元素数据
    if (typeName.indexOf("getElements") >= 0) {
      ret.allElements = serviceRet.data.elements ?? [];
      if (returnType.length > 0) {
        ret.allElements = ret.allElements.filter(ele => returnType.find(type => type === ele.type))
      }
      if (ret.allElements.length > 0) {
        ret.defaultEntityId = ret.allElements[0].params?.entityRefId;
        ret.allElements.forEach(ele => {
          ele.entityId = ele.params?.entityRefId;
          if (ele.type === ElementType.FACE) {
            // 组织面数据，根据index和position，并返回默认法向
            ele.points = [];
            ele.defaultNormal = {x: ele.params.normal[0], y: ele.params.normal[1], z: ele.params.normal[2]}
            this.getTrianglesByFace([ele])
          }
          if (ele.type === ElementType.EDGE || ele.type === ElementType.CURVE) {
            // 线数据
            ele.lineLength = ele.params.length;
            ele.sPoint = {
              x: ele.params.startPoint[0],
              y: ele.params.startPoint[1],
              z: ele.params.startPoint[2]
            }
            ele.ePoint = {
              x: ele.params.endPoint[0],
              y: ele.params.endPoint[1],
              z: ele.params.endPoint[2]
            }
            ele.mPoint = {
              x: ele.params.middlePoint[0],
              y: ele.params.middlePoint[1],
              z: ele.params.middlePoint[2]
            }
          }
          if (ele.type === ElementType.VERTEX) {
            // 顶点
            ele.point = ele.params.position
          }
          if (ele.type === ElementType.DATUMPLANE) {
            // 基准面
            ele.normal = ele.params.normal
          }
        })
      }
    }
    // 解析实体数据
    if (typeName.indexOf("getEntities") >= 0) {
      ret.entities = serviceRet.data.entities ?? [];
      ret.defaultEntityId = ret.entities[0]?.id
    }
    return ret;
  }

  /**
   * 测试代码
   * @returns {Promise<void>}
   */
  async testFace() {

  }

  /**
   * 定义拾取方法
   */
  handlePickElement() {
    this.event.subscribeElementPickEvent(async e => {
      eventBus.emit("selectElement", e)
    })
  }

  /**
   * 获取特征名称
   * @param featureName
   * @returns {Promise<*>}
   */
  async getFeatureMaxName(featureName) {
    let ret = [];
    let featureRet = await this.request.execFunction(PluginCADToolFunctionType.GETALLFEATURES, {});
    if (featureRet.code === 200) {
      let features = featureRet.data.filter(feature => feature.name.indexOf(featureName) > -1)
      if (features.length === 0) {
        return featureName;
      }
      // 返回最大编码特征名
      features.forEach(feature => {
        let match = feature.name.match(/\d+$/)
        ret.push(match ? parseInt(match[0]) : 0)
      })
      return featureName + (Math.max(...ret) + 1)
    }
    return featureName;
  }

  /**
   * 检测参数值有效性
   * @returns {string}
   */
  validateTrussParams() {
    let error = "";
    switch (this.type) {
      case "typeT":
        if (!this.sizeParam.typeT.B || this.sizeParam.typeT.B < 1) {
          error = "BError";
          break;
        }
        if (!this.sizeParam.typeT.H || this.sizeParam.typeT.H < 1) {
          error = "HError";
          break;
        }
        if (!this.sizeParam.typeT.S || this.sizeParam.typeT.S < 1) {
          error = "SError";
          break;
        }
        if (!this.sizeParam.typeT.S1 || this.sizeParam.typeT.S1 < 1) {
          error = "S1Error";
          break;
        }
        if (parseFloat(this.sizeParam.typeT.H) <= parseFloat(this.sizeParam.typeT.S)) {
          error = "HSError";
          break;
        }
        if (parseFloat(this.sizeParam.typeT.B) <= parseFloat(this.sizeParam.typeT.S1)) {
          error = "BS1Error";
          break;
        }
        break;
      case "typeG":
        if (!this.sizeParam.typeG.B || this.sizeParam.typeG.B < 1) {
          error = "BError";
          break;
        }
        if (!this.sizeParam.typeG.H || this.sizeParam.typeG.H < 1) {
          error = "HError";
          break;
        }
        if (!this.sizeParam.typeG.S || this.sizeParam.typeG.S < 1) {
          error = "SError";
          break;
        }
        if (!this.sizeParam.typeG.S1 || this.sizeParam.typeG.S1 < 1) {
          error = "S1Error";
          break;
        }
        if (!this.sizeParam.typeG.S2 || this.sizeParam.typeG.S2 < 1) {
          error = "S2Error";
          break;
        }
        if (parseFloat(this.sizeParam.typeG.B) <= parseFloat(this.sizeParam.typeG.S)) {
          error = "BSError";
          break;
        }
        if ((parseFloat(this.sizeParam.typeG.S1) + parseFloat(this.sizeParam.typeG.S2))
          >= parseFloat(this.sizeParam.typeG.H)) {
          error = "HS1S2Error";
          break;
        }
        break;
      case "typeC":
        if (!this.sizeParam.typeC.B || this.sizeParam.typeC.B < 1) {
          error = "BError";
          break;
        }
        if (!this.sizeParam.typeC.H || this.sizeParam.typeC.H < 1) {
          error = "HError";
          break;
        }
        if (!this.sizeParam.typeC.h || this.sizeParam.typeC.h < 1) {
          error = "hError";
          break;
        }
        if (!this.sizeParam.typeC.S || this.sizeParam.typeC.S < 1) {
          error = "SError";
          break;
        }
        if (!this.sizeParam.typeC.S1 || this.sizeParam.typeC.S1 < 1) {
          error = "S1Error";
          break;
        }
        if (!this.sizeParam.typeC.S2 || this.sizeParam.typeC.S2 < 1) {
          error = "S2Error";
          break;
        }
        if (parseFloat(this.sizeParam.typeC.H) <= parseFloat(this.sizeParam.typeC.S)) {
          error = "HSError";
          break;
        }
        if (parseFloat(this.sizeParam.typeC.h) <= parseFloat(this.sizeParam.typeC.S)) {
          error = "hSError";
          break;
        }
        if ((parseFloat(this.sizeParam.typeC.S1) + parseFloat(this.sizeParam.typeC.S2))
          >= parseFloat(this.sizeParam.typeC.B)) {
          error = "BS1S2Error";
          break;
        }
        break;
      case "typeJ":
        if (!this.sizeParam.typeJ.B || this.sizeParam.typeJ.B < 1) {
          error = "BError";
          break;
        }
        if (!this.sizeParam.typeJ.H || this.sizeParam.typeJ.H < 1) {
          error = "HError";
          break;
        }
        if (!this.sizeParam.typeJ.S || this.sizeParam.typeJ.S < 1) {
          error = "SError";
          break;
        }
        if (!this.sizeParam.typeJ.b || this.sizeParam.typeJ.b < 1) {
          error = "bError";
          break;
        }
        if (!this.sizeParam.typeJ.b1 || this.sizeParam.typeJ.b1 < 1) {
          error = "b1Error";
          break;
        }
        if (!this.sizeParam.typeJ.b2 || this.sizeParam.typeJ.b2 < 1) {
          error = "b2Error";
          break;
        }
        if ((parseFloat(this.sizeParam.typeJ.b1) + parseFloat(this.sizeParam.typeJ.b2))
          >= parseFloat(this.sizeParam.typeJ.B)) {
          error = "Bb1b2Error";
          break;
        }
        if (parseFloat(this.sizeParam.typeJ.B) <= parseFloat(this.sizeParam.typeJ.b)) {
          error = "BbError";
          break;
        }
        if (parseFloat(this.sizeParam.typeJ.H) < parseFloat(this.sizeParam.typeJ.S) * 2) {
          error = "SLargeError";
          break;
        }
        break;
      case "typeVL":
      case "typeLL":
      case "typeSL":
        if (!this.sizeParam.typeL.B || this.sizeParam.typeL.B < 1) {
          error = "BError";
          break;
        }
        if (!this.sizeParam.typeL.H || this.sizeParam.typeL.H < 1) {
          error = "HError";
          break;
        }
        if (!this.sizeParam.typeL.S || this.sizeParam.typeL.S < 1) {
          error = "SError";
          break;
        }
        if (this.type === "typeVL" && (!this.sizeParam.typeL.S1 || this.sizeParam.typeL.S1 < 1)) {
          error = "S1Error";
          break;
        }
        if (parseFloat(this.sizeParam.typeL.H) <= parseFloat(this.sizeParam.typeL.S)) {
          error = "HSError";
          break;
        }
        if (parseFloat(this.sizeParam.typeL.B) <= parseFloat(this.sizeParam.typeL.S1)) {
          error = "BS1Error";
          break;
        }
        if (this.type === "typeSL" && (this.sizeParam.typeL.x >= 90 || this.sizeParam.typeL.x <= 0)) {
          error = "closedAngleError";
          break;
        }
        if (this.type === "typeLL" && (this.sizeParam.typeL.x >= 180 || this.sizeParam.typeL.x <= 90)) {
          error = "obtuseAngleError";
          break;
        }
        break;
      case "typeZ":
        if (!this.sizeParam.typeZ.B || this.sizeParam.typeZ.B < 1) {
          error = "BError";
          break;
        }
        if (!this.sizeParam.typeZ.b || this.sizeParam.typeZ.b < 1) {
          error = "bError";
          break;
        }
        if (!this.sizeParam.typeZ.H || this.sizeParam.typeZ.H < 1) {
          error = "HError";
          break;
        }
        if (!this.sizeParam.typeZ.S || this.sizeParam.typeZ.S < 1) {
          error = "SError";
          break;
        }
        if (!this.sizeParam.typeZ.S1 || this.sizeParam.typeZ.S1 < 1) {
          error = "S1Error";
          break;
        }
        if (!this.sizeParam.typeZ.S2 || this.sizeParam.typeZ.S2 < 1) {
          error = "S2Error";
          break;
        }
        if (parseFloat(this.sizeParam.typeZ.b) <= parseFloat(this.sizeParam.typeZ.S)) {
          error = "bSError";
          break;
        }
        if (parseFloat(this.sizeParam.typeZ.B) <= parseFloat(this.sizeParam.typeZ.S)) {
          error = "BSError";
          break;
        }
        if ((parseFloat(this.sizeParam.typeZ.S1) + parseFloat(this.sizeParam.typeZ.S2))
          >= parseFloat(this.sizeParam.typeZ.H)) {
          error = "HS1S2Error";
          break;
        }
        break;
    }
    return error;
  }

  /**
   *  检测界面元素选择的有效性
   * @returns {string}
   */
  validateElementsSelect() {
    if (this.selectedParam.curveFace.elements?.length < 1) {
      return "face";
    }
    if (!this.selectedParam.startPoint.elements?.[0]) {
      return "startPoint";
    }
    if (!this.selectedParam.endPoint.elements?.[0]) {
      return "endPoint";
    }
    if (this.selectedParam.path.allElementIds?.length < 1) {
      return "curve";
    }
  }

  /**
   * 创建长桁入口
   * @returns {Promise<{errorMessage}>}
   */
  async createLongTruss() {
    let subStarct = [];
    let trussName = "";
    try {
      // 切割长桁贴合面并获取相关参数
      let {
        cutFaceLargeRet,
        cutFaceRet,
        direction,
        exturdeFaces,
        sPoint,
        ePoint,
        outward,
        height,
        cutWidth,
        tangentNormal,
      }
        = await this.getBaseCutFace();
      // 创建具体类型的长桁
      switch (this.type) {
        case "typeT":
          trussName = this.names.stringerName.typeT;
          subStarct = await this.createTrussByT(cutFaceRet, direction, exturdeFaces, sPoint, outward);
          break;
        case "typeG":
          trussName = this.names.stringerName.typeG;
          subStarct = await this.createTrussByG(cutFaceRet, direction, exturdeFaces, sPoint, outward);
          break;
        case "typeZ":
          trussName = this.names.stringerName.typeZ;
          subStarct = await this.createTrussByZ(cutFaceRet, direction, exturdeFaces, sPoint, outward, cutWidth);
          break;
        case "typeC":
          trussName = this.names.stringerName.typeC;
          subStarct = await this.createTrussByC(cutFaceRet, direction, exturdeFaces, sPoint, outward, cutWidth);
          break;
        case "typeVL":
          trussName = this.names.stringerName.typeVL;
          subStarct = await this.createTrussByVL(cutFaceRet, direction, exturdeFaces, sPoint, outward)
          break;
        case "typeSL":
          trussName = this.names.stringerName.typeSL;
          await this.createTrussBySL(cutFaceRet, direction, exturdeFaces, sPoint, outward, cutWidth, ePoint, tangentNormal)
          break;
        case "typeLL":
          trussName = this.names.stringerName.typeLL;
          await this.createTrussByLL(cutFaceRet, direction, exturdeFaces, sPoint, outward, cutWidth, ePoint, tangentNormal)
          break;
        case "typeJ":
          trussName = this.names.stringerName.typeJ;
          await this.createTrussByJ(cutFaceRet, direction, exturdeFaces, sPoint, outward, tangentNormal)
          break;
      }

      // 脱模获取长桁
      if (subStarct?.length > 0) {
        await this.moldRelease(cutFaceLargeRet.id, height + 4, outward, subStarct, trussName)
      }
    } catch (e) {
      return {
        errorMessage: e
      }
    }
  }

  /**
   * 脱模获取长桁
   * @param largeFaceId
   * @param height
   * @param outward
   * @param subStarct
   * @param trussName
   * @returns {Promise<number>}
   */
  async moldRelease(largeFaceId, height, outward, subStarct, trussName) {
    // 长桁外壳1 ，通过复制大面进行处理
    let entityId1 = await this.thickenSurfaces(largeFaceId, height, outward, true)
    // 脱模第一步，布尔操作
    let solid1 = await this.getIncrementServiceByName("booleanSubtract", true, "",
      {
        entityBs: subStarct,
        entityA: entityId1,
        keep: 0
      })
    // 长桁外壳2 ，通过大面进行处理
    let entityId2 = await this.thickenSurfaces(largeFaceId, height, outward, false)
    // 脱模第二步，布尔操作
    let solid2 = await this.getIncrementServiceByName("booleanSubtract", true,
      await this.getFeatureMaxName(trussName),
      {
        entityBs: [solid1.entityId],
        entityA: entityId2,
        keep: 0
      })
  }

  /**
   * T字
   * @param cutFaceRet
   * @param direction
   * @param exturdeFaces
   * @param sPoint
   * @param outward
   * @returns {Promise<number[]|*[]>}
   */
  async createTrussByT(cutFaceRet, direction, exturdeFaces, sPoint, outward) {
    // 1、获取竖的底面并加厚
    let face1 = await this.getMiddleFace({
        distance: this.sizeParam.typeT.S1 / 2,
        surfaceIds: exturdeFaces,
        outward: direction.left,
        pointInFace: true
      },
      {
        distance: this.sizeParam.typeT.S1 / 2,
        surfaceIds: exturdeFaces,
        outward: direction.right,
        pointInFace: true
      }, cutFaceRet, sPoint)
    let entity1 = await this.thickenSurfaces(face1?.id, this.sizeParam.typeT.H, outward, false)
    // 2、横并加厚
    let entity2 = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeT.S, outward, false)
    return [entity1, entity2]
  }

  /**
   * 工字
   * @param cutFaceRet
   * @param direction
   * @param exturdeFaces
   * @param sPoint
   * @param outward
   * @returns {Promise<(number|number)[]|*[]>}
   */
  async createTrussByG(cutFaceRet, direction, exturdeFaces, sPoint, outward) {
    // 1、获取竖的底面并加厚
    let face1 = await this.getMiddleFace({
        distance: this.sizeParam.typeG.S / 2,
        surfaceIds: exturdeFaces,
        outward: direction.left,
        pointInFace: true
      },
      {
        distance: this.sizeParam.typeG.S / 2,
        surfaceIds: exturdeFaces,
        outward: direction.right,
        pointInFace: true
      }, cutFaceRet, sPoint)
    let entity1 = await this.thickenSurfaces(face1?.id, this.sizeParam.typeG.H - 0.5, outward, false)

    // 2、获取下横长桁
    let entity2 = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeG.S1, outward, true)

    // 3、获取上横长桁，需要布尔运算
    let entity31 = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeG.H, outward, true)
    let entity32 = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeG.H - this.sizeParam.typeG.S2
      , outward, false)
    let {entityId} = await this.getIncrementServiceByName("booleanSubtract", true,
      "", {
        entityBs: [entity32],
        entityA: entity31,
        keep: 0
      })
    return [entity1, entity2, entityId]
  }

  /**
   * 直角L
   * @param cutFaceRet
   * @param direction
   * @param exturdeFaces
   * @param sPoint
   * @param outward
   * @returns {Promise<number[]>}
   */
  async createTrussByVL(cutFaceRet, direction, exturdeFaces, sPoint, outward) {
    // 方向参数，0表示H端在左，1表示H端在右；偏移到最左切面，往右进行开切
    let moveDirection = direction.isSame ? 0 : 1;
    let moveDistance = this.sizeParam.typeL.direction !== 1 ? this.sizeParam.typeL.S1 :
      this.sizeParam.typeL.B - this.sizeParam.typeL.S1;
    let isPointInFace = this.sizeParam.typeL.direction !== 1;
    // 偏移曲面到左侧
    let faceRet = await this.offsetSurfaces(exturdeFaces,
      this.sizeParam.typeL.B / 2, direction.isSame ? 1 : 0, false)
    // 竖方向
    let face1 = await this.getSplitedSurface(moveDistance, faceRet.faceIds, moveDirection,
      cutFaceRet, direction.edgeDirection.leftPoint.position, isPointInFace, true)
    let entity1 = await this.thickenSurfaces(face1?.id, this.sizeParam.typeL.H, outward, false)

    // 横方向
    let entity2 = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeL.S, outward, false)
    return [entity1, entity2]
  }

  /**
   * 锐角L
   * @param cutFaceRet
   * @param direction
   * @param exturdeFaces
   * @param sPoint
   * @param outward
   * @param cutWidth
   * @param ePoint
   * @returns {Promise<number[]>}
   */
  async createTrussBySL(cutFaceRet, direction, exturdeFaces, sPoint, outward, cutWidth, ePoint, tangentNormal) {
    // 偏移曲面到左侧
    let faceRet = await this.offsetSurfaces(exturdeFaces, this.sizeParam.typeL.B / 2,
      direction.isSame ? 1 : 0)
    let moveDirection = 0;
    // 计算切面移动位置
    let leftPoint = direction.edgeDirection.leftPoint.position;
    let rightPoint = direction.edgeDirection.rightPoint.position;
    let {
      moveLength,
      lineAndPointDistance,
      faceMoveDirection
    } = this.getRotatePointByAxisAndAngel(direction.faceNormal, this.sizeParam.typeL.x,
      direction.isSame ? leftPoint : rightPoint,
      direction.isSame ? rightPoint : leftPoint,
      this.sizeParam.typeL.H, this.sizeParam.typeL.direction)
    // 偏移完整曲面
    let offsetFace = await this.offsetSurfaces([this.selectedParam.curveFace.elements[0].id],
      lineAndPointDistance, outward === 1 ? 0 : 1)
    // 移动切割面
    if (faceMoveDirection === 0) {
      // 需要右边移动
      moveDirection = direction.isSame ? 0 : 1
    } else {
      moveDirection = direction.isSame ? 1 : 0
    }
    let newCutFace = await this.offsetSurfaces(faceRet.faceIds, moveLength, moveDirection);
    // 完整曲面分割切面 需要进行循环
    let faceData = await this.splitSurface(newCutFace.entityId, offsetFace.entityId)
    let faceEntity = this.getNeedFace(faceData.filter(ele => ele.type === ElementType.FACE), sPoint, true)
    let linesRet = await this.getQueryServiceByType("getElementsByEntityId", faceEntity.entityId, [ElementType.EDGE])
    // 隐藏实体
    let hideEntities = faceData.filter(j => j.type === EntityType.FACE).map(j => j.params.entityRefId)
    await this.getIncrementServiceByName("setEntityVisible", false, "", {
      entityIds: hideEntities,
      status: 0
    })
    // 获取斜边底端曲线
    let surfaceLinesRet = await this.getQueryServiceByType("getElementsByEntityId", cutFaceRet.entityId, [ElementType.EDGE])
    let bottomLines = this.getLinesByFaceAndPoint(surfaceLinesRet.allElements,
      this.sizeParam.typeL.direction === 0 ? leftPoint : rightPoint, sPoint, ePoint, cutWidth)
    let lines = this.getDoubleLines(bottomLines, linesRet.allElements)
    // 放样两条线
    let faceEntities = [];
    for (const j of lines) {
      let face = await this.loftTheSurface(j.lineA.id, j.lineB.id)
      faceEntities.push(face.entityId)
    }
    // 缝合斜曲面并计算加厚方向
    let obliqueFace = await this.knitTheSurfaces(faceEntities, sPoint,
      this.sizeParam.typeL.direction === 0 ? leftPoint : rightPoint, null, null)
    // 缝合所有面
    let glueFace = await this.knitTheSurfaces([obliqueFace.entityId, cutFaceRet.entityId], sPoint, null,
      tangentNormal, outward)
    // 加厚所有面
    let name = await this.getFeatureMaxName(this.names.stringerName.typeSL)
    await this.thickenSurfaces(glueFace.entityId, this.sizeParam.typeL.S, glueFace.thickenReverse, false, name)
  }

  /**
   * 钝角L
   * @param cutFaceRet
   * @param direction
   * @param exturdeFaces
   * @param sPoint
   * @param outward
   * @param cutWidth
   * @param ePoint
   * @param tangentNormal
   * @returns {Promise<number[]>}
   */
  async createTrussByLL(cutFaceRet, direction, exturdeFaces, sPoint, outward, cutWidth, ePoint, tangentNormal) {
    // 偏移曲面到左侧
    let faceRet = await this.offsetSurfaces(exturdeFaces, this.sizeParam.typeL.B / 2,
      direction.isSame ? 1 : 0)
    let moveDirection = 0;

    // 计算切面移动位置
    let leftPoint = direction.edgeDirection.leftPoint.position;
    let rightPoint = direction.edgeDirection.rightPoint.position;
    let {
      moveLength,
      lineAndPointDistance,
      faceMoveDirection
    } = this.getRotatePointByAxisAndAngel(direction.faceNormal, this.sizeParam.typeL.x,
      direction.isSame ? leftPoint : rightPoint,
      direction.isSame ? rightPoint : leftPoint,
      this.sizeParam.typeL.H, this.sizeParam.typeL.direction)

    // 偏移完整曲面
    let offsetFace = await this.offsetSurfaces([this.selectedParam.curveFace.elements[0].id],
      lineAndPointDistance, outward === 1 ? 0 : 1)
    // 移动切割面
    if (faceMoveDirection === 0) {
      // 需要右边移动
      moveDirection = direction.isSame ? 0 : 1
    } else {
      moveDirection = direction.isSame ? 1 : 0
    }
    let newCutFace = await this.offsetSurfaces(faceRet.faceIds, moveLength, moveDirection);
    // 完整曲面分割切面 需要进行循环
    let faceData = await this.splitSurface(newCutFace.entityId, offsetFace.entityId)
    let faceEntity = this.getNeedFace(faceData.filter(ele => ele.type === ElementType.FACE), sPoint, true)
    let linesRet = await this.getQueryServiceByType("getElementsByEntityId", faceEntity.entityId, [ElementType.EDGE])
    // 隐藏实体
    let hideEntities = faceData.filter(j => j.type === EntityType.FACE).map(j => j.params.entityRefId)
    await this.getIncrementServiceByName("setEntityVisible", false, "", {
      entityIds: hideEntities,
      status: 0
    })
    // 获取斜边底端曲线
    let surfaceLinesRet = await this.getQueryServiceByType("getElementsByEntityId", cutFaceRet.entityId, [ElementType.EDGE])
    let bottomLines = this.getLinesByFaceAndPoint(surfaceLinesRet.allElements,
      this.sizeParam.typeL.direction === 0 ? leftPoint : rightPoint, sPoint, ePoint, cutWidth)

    let lines = this.getDoubleLines(bottomLines, linesRet.allElements)
    // 放样两条线
    let faceEntities = [];
    for (const j of lines) {
      let face = await this.loftTheSurface(j.lineA.id, j.lineB.id)
      faceEntities.push(face.entityId)
    }
    // let retData = await this.knitTheSurfaces(faceEntities, sPoint,
    //   this.sizeParam.typeL.direction === 0 ? leftPoint : rightPoint)
    // let entityA = await this.thickenSurfaces(retData.entityId, this.sizeParam.typeL.S1, retData.thickenReverse, false)
    // // 底面
    // let entityB = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeL.S, outward, false)
    // // 需要切割entityA
    // return [entityA, entityB]

    // 缝合斜曲面并计算加厚方向
    let obliqueFace = await this.knitTheSurfaces(faceEntities, sPoint,
      this.sizeParam.typeL.direction === 0 ? leftPoint : rightPoint, null, null)
    // 缝合所有面
    let glueFace = await this.knitTheSurfaces([obliqueFace.entityId, cutFaceRet.entityId], sPoint, null,
      tangentNormal, outward)
    // 加厚所有面
    let name = await this.getFeatureMaxName(this.names.stringerName.typeLL)
    await this.thickenSurfaces(glueFace.entityId, this.sizeParam.typeL.S, glueFace.thickenReverse, false, name)
  }

  /**
   * 几字
   * @param cutFaceRet
   * @param direction
   * @param exturdeFaces
   * @param sPoint
   * @param outward
   * @param tangentNormal
   * @returns {Promise<void>}
   */
  async createTrussByJ(cutFaceRet, direction, exturdeFaces, sPoint, outward, tangentNormal) {
    // 移动切面到最左侧
    let faceRet = await this.offsetSurfaces(exturdeFaces, this.sizeParam.typeJ.B / 2,
      direction.isSame ? 1 : 0)
    // 面1
    let face1 = await this.getSplitedSurface(this.sizeParam.typeJ.b1, faceRet.faceIds, direction.isSame ? 0 : 1,
      cutFaceRet, direction.edgeDirection.leftPoint.position, true, true)
    let face1Lines = await this.getQueryServiceByType("getElementsByEntityId", face1?.entityId, [ElementType.EDGE])
    // 面2
    let face2 = await this.getSplitedSurface(this.sizeParam.typeJ.B - this.sizeParam.typeJ.b2, faceRet.faceIds,
      direction.isSame ? 0 : 1, cutFaceRet, direction.edgeDirection.leftPoint.position, false, true)
    let face2Lines = await this.getQueryServiceByType("getElementsByEntityId", face2?.entityId, [ElementType.EDGE])
    // 面3
    let face31 = await this.getSplitedSurface(this.sizeParam.typeJ.B / 2 - this.sizeParam.typeJ.b / 2 + this.sizeParam.typeJ.S * 0.5,
      faceRet.faceIds, direction.isSame ? 0 : 1, cutFaceRet, direction.edgeDirection.leftPoint.position, false, false)
    let face32 = await this.getSplitedSurface(this.sizeParam.typeJ.B / 2 + this.sizeParam.typeJ.b / 2 - this.sizeParam.typeJ.S * 0.5,
      faceRet.faceIds, direction.isSame ? 0 : 1, face31, direction.edgeDirection.leftPoint.position, true, false)
    let face3 = await this.offsetSurfaces([face32?.id], this.sizeParam.typeJ.H - this.sizeParam.typeJ.S,
      outward === 0 ? 1 : 0, true)
    // 隐藏偏移前的面
    await this.getIncrementServiceByName("setEntityVisible", false, "", {
      entityIds: [face32?.entityId],
      status: 0
    })
    let face3Lines = await this.getQueryServiceByType("getElementsByEntityId", face3?.entityId, [ElementType.EDGE])
    // 放样操作，计算面4和面5
    let face45 = [], surfaces = [];
    for (const j of this.selectedParam.path.elements) {
      let targetPoint = {
        x: j.params.middlePoint[0],
        y: j.params.middlePoint[1],
        z: j.params.middlePoint[2]
      };
      // face1放样face3
      let firstLine = this.getNearestLineByPoint(targetPoint, face1Lines)
      let first1Line = this.getNearestLineByPoint(firstLine.mPoint, face3Lines)
      face45.push(await this.loftTheSurface(firstLine.id, first1Line.id))
      // face2放样face3
      let secondLine = this.getNearestLineByPoint(targetPoint, face2Lines)
      let second1Line = this.getNearestLineByPoint(secondLine.mPoint, face3Lines)
      face45.push(await this.loftTheSurface(secondLine.id, second1Line.id))
    }
    // 缝合所有曲面
    surfaces.push(face1.entityId)
    surfaces.push(face2.entityId)
    surfaces.push(face3Lines.defaultEntityId)
    surfaces.push(...face45.map(face => face.entityId))
    let knifSurface = await this.getIncrementServiceByName("knitSurface", true, "", {
      surfaces,
      controlGap: 1,
      tolerance: 0.1
    })
    // 计算出指定面的法向
    if (knifSurface.elements.length > 0) {
      let data = this.getNeedFace(knifSurface.elements.filter(j => j.type === ElementType.FACE), direction.edgeDirection.leftPoint.position, false)
      let out = outward;
      if (this.calTwoVectorsAngel(data.defaultNormal, tangentNormal) > 90) {
        out = out === 0 ? 1 : 0;
      }
      // 加厚并生成长桁
      let name = await this.getFeatureMaxName(this.names.stringerName.typeJ)
      await this.thickenSurfaces(knifSurface.entityId, this.sizeParam.typeJ.S, out, false, name)
    }
  }

  /**
   * C字
   * @param cutFaceRet
   * @param direction
   * @param exturdeFaces
   * @param sPoint
   * @param outward
   * @param cutWidth
   * @returns {Promise<(number|number)[]|number[]>}
   */
  async createTrussByC(cutFaceRet, direction, exturdeFaces, sPoint, outward, cutWidth) {
    // 偏移切面到最左端
    let moveDirection = direction.isSame ? 0 : 1;
    let faceRet0 = await this.offsetSurfaces(exturdeFaces, cutWidth / 2, direction.isSame ? 1 : 0)
    // 方向参数，direction为开口朝向，默认是0，0是H贴合；1是h贴合，2是B贴合
    // 当0或者1的时候，由于h和H的大小不同，需要区分处理
    if (this.sizeParam.typeC.direction === 0) {
      // H端贴合
      // 1、处理B边并加厚
      let faceB = await this.getSplitedSurface(this.sizeParam.typeC.S, faceRet0?.faceIds, moveDirection,
        cutFaceRet, direction.edgeDirection.leftPoint.position, true, true)
      let entityB = await this.thickenSurfaces(faceB?.id, this.sizeParam.typeC.B - 0.1, outward, false)

      // 2、处理h端，需要布尔切割
      let entityh = 0;
      let entityh1 = 0
      let entityh2 = 0;
      if (this.sizeParam.typeC.h === cutWidth) {
        entityh1 = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeC.B, outward, true)
        entityh2 = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeC.B - this.sizeParam.typeC.S2
          , outward, true)
      } else {
        // 需要切割底面
        let faceh = await this.getSplitedSurface(this.sizeParam.typeC.h, faceRet0?.faceIds, moveDirection,
          cutFaceRet, direction.edgeDirection.leftPoint.position, true, true)
        entityh1 = await this.thickenSurfaces(faceh?.id, this.sizeParam.typeC.B, outward, true)
        entityh2 = await this.thickenSurfaces(faceh?.id, this.sizeParam.typeC.B - this.sizeParam.typeC.S2
          , outward, false)
      }

      let {entityId} = await this.getIncrementServiceByName("booleanSubtract", true,
        "", {
          entityBs: [entityh2],
          entityA: entityh1,
          keep: 0
        })
      entityh = entityId

      // 3、处理H端
      let entityH = 0;
      if (this.sizeParam.typeC.H === cutWidth) {
        entityH = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeC.S1, outward, false)
      } else {
        // 需要切割底面
        let faceH = await this.getSplitedSurface(this.sizeParam.typeC.H, faceRet0?.faceIds, moveDirection,
          cutFaceRet, direction.edgeDirection.leftPoint.position, true, false)
        entityH = await this.thickenSurfaces(faceH?.id, this.sizeParam.typeC.S1, outward, false)
      }

      return [entityB, entityh, entityH]
    } else if (this.sizeParam.typeC.direction === 1) {
      // h端贴合
      // 1、处理B边并加厚
      let faceB = await this.getSplitedSurface(cutWidth - this.sizeParam.typeC.S - 0.1, faceRet0?.faceIds,
        moveDirection, cutFaceRet, direction.edgeDirection.leftPoint.position, false, true)
      let entityB = await this.thickenSurfaces(faceB?.id, this.sizeParam.typeC.B, outward, false)

      // 2、处理H端
      let entityH = 0;
      let entityH1 = 0;
      let entityH2 = 0;
      if (this.sizeParam.typeC.H === cutWidth) {
        entityH1 = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeC.B, outward, true)
        entityH2 = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeC.B - this.sizeParam.typeC.S1
          , outward, true)
      } else {
        // 需要切割底面
        let faceH = await this.getSplitedSurface(cutWidth - this.sizeParam.typeC.H, faceRet0?.faceIds, moveDirection,
          cutFaceRet, direction.edgeDirection.leftPoint.position, false, true)
        entityH1 = await this.thickenSurfaces(faceH?.id, this.sizeParam.typeC.B, outward, true)
        entityH2 = await this.thickenSurfaces(faceH?.id, this.sizeParam.typeC.B - this.sizeParam.typeC.S1
          , outward, false)
      }
      let {entityId} = await this.getIncrementServiceByName("booleanSubtract", true,
        "", {
          entityBs: [entityH2],
          entityA: entityH1,
          keep: 0
        })
      entityH = entityId

      // 3、处理h端，需要布尔切割
      let entityh = 0;
      if (this.sizeParam.typeC.h === cutWidth) {
        entityh = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeC.S2, outward, false)
      } else {
        // 需要切割底面
        let faceh = await this.getSplitedSurface(cutWidth - this.sizeParam.typeC.h, faceRet0?.faceIds, moveDirection,
          cutFaceRet, direction.edgeDirection.leftPoint.position, false, false)
        entityh = await this.thickenSurfaces(faceh?.id, this.sizeParam.typeC.S2, outward, false)
      }
      return [entityB, entityh, entityH]
    } else {
      // B端贴合，h端在左，H端在右边
      // 1、h端创建并加厚
      let faceh = await this.getSplitedSurface(this.sizeParam.typeC.S2, faceRet0?.faceIds, moveDirection,
        cutFaceRet, direction.edgeDirection.leftPoint.position, true, true)
      let entityh = await this.thickenSurfaces(faceh?.id, this.sizeParam.typeC.h, outward, false)
      // 2、H端创建并加厚
      let faceH = await this.getSplitedSurface(this.sizeParam.typeC.B - this.sizeParam.typeC.S1, faceRet0?.faceIds, moveDirection,
        cutFaceRet, direction.edgeDirection.leftPoint.position, false, true)
      let entityH = await this.thickenSurfaces(faceH?.id, this.sizeParam.typeC.H, outward, false)
      // 3、B端创建并加厚
      let entityB = await this.thickenSurfaces(cutFaceRet.id, this.sizeParam.typeC.S, outward, false)
      return [entityB, entityh, entityH]
    }
  }

  /**
   * Z字
   * @returns {Promise<void>}
   */
  async createTrussByZ(cutFaceRet, direction, exturdeFaces, sPoint, outward, cutWidth) {
    // 偏移切面到最左侧
    let faceRet = await this.offsetSurfaces(exturdeFaces, cutWidth / 2, direction.isSame ? 1 : 0)
    let moveDirection = direction.isSame ? 0 : 1;
    // 方向参数，0表示b在左，1表示B在左
    if (this.sizeParam.typeZ.direction === 0) {
      // 左侧长桁并加厚
      let outFace1 = await this.getSplitedSurface(this.sizeParam.typeZ.b - this.sizeParam.typeZ.S, faceRet?.faceIds, moveDirection,
        cutFaceRet, direction.edgeDirection.leftPoint.position, true, true)
      let entity1 = await this.thickenSurfaces(outFace1?.id, this.sizeParam.typeZ.H - this.sizeParam.typeZ.S2, outward, false)

      let inFace2 = await this.getSplitedSurface(this.sizeParam.typeZ.b, faceRet?.faceIds, moveDirection,
        cutFaceRet, direction.edgeDirection.leftPoint.position, true, true)
      let entity2 = await this.thickenSurfaces(inFace2?.id, this.sizeParam.typeZ.H, outward, false)

      // 布尔操作
      let {entityId} = await this.getIncrementServiceByName("booleanSubtract", true,
        "", {
          entityBs: [entity1],
          entityA: entity2,
          keep: 0
        })

      // 下横长桁并加厚
      let inFace3 = await this.getSplitedSurface(this.sizeParam.typeZ.b - this.sizeParam.typeZ.S, faceRet?.faceIds, moveDirection,
        cutFaceRet, direction.edgeDirection.leftPoint.position, false, false)
      let entity3 = await this.thickenSurfaces(inFace3?.id, this.sizeParam.typeZ.S1, outward, false)

      return [entityId, entity3]
    } else {
      // 右侧长桁并加厚
      let outFace1 = await this.getSplitedSurface(this.sizeParam.typeZ.B, faceRet.faceIds, moveDirection,
        cutFaceRet, direction.edgeDirection.leftPoint.position, false, true)
      let entity1 = await this.thickenSurfaces(outFace1?.id, this.sizeParam.typeZ.H - this.sizeParam.typeZ.S2, outward, false)

      let inFace2 = await this.getSplitedSurface(this.sizeParam.typeZ.B - this.sizeParam.typeZ.S, faceRet.faceIds, moveDirection,
        cutFaceRet, direction.edgeDirection.leftPoint.position, false, true)
      let entity2 = await this.thickenSurfaces(inFace2?.id, this.sizeParam.typeZ.H, outward, false)

      // 布尔操作
      let {entityId} = await this.getIncrementServiceByName("booleanSubtract", true,
        "", {
          entityBs: [entity1],
          entityA: entity2,
          keep: 0
        })

      // 下横长桁并加厚
      let inFace3 = await this.getSplitedSurface(this.sizeParam.typeZ.B, faceRet.faceIds, moveDirection,
        cutFaceRet, direction.edgeDirection.leftPoint.position, true, false)
      let entity3 = await this.thickenSurfaces(inFace3?.id, this.sizeParam.typeZ.S1, outward, false)

      return [entityId, entity3]
    }
  }

  /**
   * 获取切割曲面
   * @param lineId
   * @param extrudedFaces
   * @param thickness
   * @param curveFace
   * @returns {Promise<number|string>}
   */
  async cutLargeSurface(lineId, extrudedFaces, thickness, curveFace) {
    // 获取起点所在的样条曲线的中点，用于后续条件判断
    let lineRet = await this.getQueryServiceByType("getElementsByIds", [lineId], []);
    let middlePoint = lineRet.allElements[0].mPoint;
    // 复制切面
    let copyFaces = await this.copySurfaceByCount(extrudedFaces);
    // 切面加厚
    let thickenRet = await this.getIncrementServiceByName("thicken", true, "", {
      thickenSurfaceId: copyFaces.find(j => j.type === ElementType.FACE)?.id,
      thickness,
      outward: 2
    })
    // 曲面切除
    let cutSplitRet = await this.getIncrementServiceByName("createCutSplit", true, "", {
      solidId: thickenRet.entityId,
      cutSurfaceId: curveFace.params.entityRefId,
      isInvert: 0
    })
    // 获取最近面
    let targetFace = this.getNeedFace(cutSplitRet.elements.filter(j => j.type === ElementType.FACE), middlePoint, false)
    // 复制切除曲面
    let cutLargeFaceRet = await this.copySurfaceByCount([targetFace.id])
    let largeFace = cutLargeFaceRet.find(j => j.type === ElementType.FACE);
    // 隐藏无需显示的内容
    await this.getIncrementServiceByName("setEntityVisible", false, "", {
      entityIds: [cutSplitRet.entityId, largeFace?.params.entityRefId],
      status: 0
    })
    return {
      id: largeFace?.id,
      entityId: largeFace?.params.entityRefId
    }
  }

  /**
   * 根据指定距离、点获取相关的面
   * @param faces
   * @param targetPoint
   * @param isMax
   * @returns {*}
   */
  getNeedFace(faces, targetPoint, isMax) {
    // 计算点到面的位置
    let targetFace = null;
    let distances = [];
    faces.forEach(face => {
      let faceDistance = [];
      face.points.forEach(points =>
        faceDistance.push(this.getDistanceToTriangle(points.point1, points.point2, points.point3, targetPoint))
      )
      face.minDistance = Math.min(...faceDistance)
      distances.push(face.minDistance)
    })
    if (isMax) {
      // 返回距离最大的面
      targetFace = faces.find(face => face.minDistance === Math.max(...distances))
    } else {
      // 返回距离最小的面
      targetFace = faces.find(face => face.minDistance === Math.min(...distances))
    }
    if (!targetFace) {
      throw "netError"
    }
    return targetFace;
  }

  /**
   * 获取相切拉取面
   * @param pointId
   * @param faceId
   * @param edgeIds
   * @param sPoint
   * @returns {Promise<*|number>}
   */
  async getExtrudedFaces(pointId, faceId, edgeIds, sPoint) {
    // 1、生成切面基准面
    let tangentDatumRet = await this.request.execFunction(PluginCADToolFunctionType.CREATETANGENTDATUMPLANE, {
      niId: parseInt(pointId),
      face2Id: faceId
    })
    if (tangentDatumRet.code !== 200 || tangentDatumRet.data === -1) {
      throw "datumError"
    }

    // 2、获取基准面法向
    let datumRet = await this.getQueryServiceByType("getElementsByFeatureId", tangentDatumRet.data, []);
    let tangentNormal = datumRet.allElements[0]?.normal;

    // 3、切面草图中转换边界
    let datumId = datumRet.allElements[0]?.id;
    // 草图命令
    let sketch = this.command.sketch;
    let sketchName = await this.getFeatureMaxName(this.names.feature.lineConvert);
    sketch.createSketch({sketchName, datumId: datumId});
    sketch.convertEdge({edgeIds: edgeIds});
    sketch.exitSketch();
    await this.command.execute()
    this.command.clearCommand();

    // 4、拉伸曲面
    let res = await this.getQueryServiceByType("getEntitiesByFeatureNames", [sketchName], [])
    let extrudeSurfaceRet = await this.getIncrementServiceByName("extrudeSurface", true, "", {
      curveIds: [res.defaultEntityId],
      directionType: 0, // 模拟法线方向
      extrudeType1: 4,  // 对称
      height1: this.extrudeLength,
    })

    // 5、隐藏拉伸后的曲面
    await this.getIncrementServiceByName("setEntityVisible", false, "", {
      entityIds: [extrudeSurfaceRet?.entityId, datumRet.defaultEntityId],
      status: 0
    })

    // 6、基于起始点获取切面法向
    let nearFace = this.getNeedFace(extrudeSurfaceRet.elements.filter(j => j.type === ElementType.FACE), sPoint, false)

    // 7、拉伸可能是一组连续面
    return {
      exturdeFaces: extrudeSurfaceRet.elements.filter(j => j.type === ElementType.FACE).map(j => j.id),
      exturdeFaceEntityId: extrudeSurfaceRet.elements[0]?.params.entityRefId,
      exturdeFaceNormal: {
        x: nearFace.params.normal?.[0],
        y: nearFace.params.normal?.[1],
        z: nearFace.params.normal?.[2]
      },
      tangentNormal,
    };
  }

  /**
   * 根据曲面数量复制曲面
   * @param surfaceIds
   * @returns {Promise<*|*[]>}
   */
  async copySurfaceByCount(surfaceIds) {
    let instanceIds = [];
    surfaceIds.forEach(id => instanceIds.push(""))
    let ret = await this.getIncrementServiceByName("copySurface", true, "", {
      surfaceIds,
      instanceIds
    })
    return ret.elements
  }

  /**
   * 分割曲面返回元素
   * @param solidId
   * @param cutSurfaceId
   * @returns {Promise<*|number>}
   */
  async splitSurface(solidId, cutSurfaceId) {
    let splitRet = await this.getIncrementServiceByName("cutSplit", true, "",
      {solidId, cutSurfaceId})
    return splitRet?.elements
  }

  /**
   * 加厚曲面，并返回实体id
   * @param thickenSurfaceId
   * @param thickness
   * @param outward
   * @param isCopy
   * @param featureName
   * @returns {Promise<number>}
   */
  async thickenSurfaces(thickenSurfaceId, thickness, outward, isCopy, featureName) {
    // 是否复制当前曲面用于加厚
    if (isCopy) {
      let elements = await this.copySurfaceByCount([thickenSurfaceId])
      let face = elements.find(j => j.type === ElementType.FACE);
      thickenSurfaceId = face?.id
    }
    // 加厚曲面
    let solidRet = await this.getIncrementServiceByName("thicken", true, featureName ?? "", {
      thickenSurfaceId, thickness, outward
    })

    return solidRet.elements[0].params?.entityRefId
  }

  /**
   * 偏移曲面，返回曲面信息
   * @param surfaceIds
   * @param distance
   * @param reverse
   * @param isHidden
   * @returns {Promise<{error: number}|{error: number}|{elements: [],faceIds:[], entityId: number, id: *, error: number}>}
   */
  async offsetSurfaces(surfaceIds, distance, reverse, isHidden) {
    let offsetRet = await this.getIncrementServiceByName("offsetSurface", true, "", {
      surfaceIds,
      distance,
      reverse
    })
    if (offsetRet.elements.length > 0) {
      offsetRet.faceIds = offsetRet.elements.filter(j => j.type === ElementType.FACE).map(j => j.id)
    }
    // 隐藏此切面
    if (!isHidden) {
      await this.getIncrementServiceByName("setEntityVisible", false, "", {
        entityIds: [offsetRet.entityId],
        status: 0
      })
    }
    return offsetRet
  }

  /**
   * 放样曲面
   * @param line1Id
   * @param line2Id
   * @returns {Promise<{elements: *[], entityId: number, id: *, error: number}|{error: number}>}
   */
  async loftTheSurface(line1Id, line2Id) {
    // 放样曲面
    return await this.getIncrementServiceByName("loftSurface", true, "", {
      profiles: [line1Id, line2Id],
      hasGuideCurve: 0,
      guideCurves: []
    })
  }

  /**
   * 缝合并加厚曲面
   * @param surfaces
   * @param point1
   * @param point2
   * @param tangentNormal
   * @param outward
   * @returns {Promise<{entityId: number, cutFaceEntityId: number, thickenReverse: number}>}
   */
  async knitTheSurfaces(surfaces, point1, point2, tangentNormal, outward) {
    // 缝合所有曲面
    let inclinedFaces = null;
    let entityId = 0;
    if (surfaces.length > 1) {
      let data = await this.getIncrementServiceByName("knitSurface", true, "", {
        surfaces,
        controlGap: 1,
        tolerance: 0.1
      })
      inclinedFaces = data.elements.filter(j => j.type === ElementType.FACE)
      entityId = data.entityId
    } else {
      let elementsRet = await this.getQueryServiceByType("getElementsByEntityId", surfaces[0], [ElementType.FACE])
      entityId = elementsRet.defaultEntityId
      inclinedFaces = elementsRet.allElements
    }

    // 计算加厚法向
    let thickenReverse = 1;
    let face = this.getNeedFace(inclinedFaces, point1, false)
    let faceNormal = {x: face.params.normal[0], y: face.params.normal[1], z: face.params.normal[2]}
    // 通过面的法向与交点到起始点的方向计算角度
    if (point2) {
      let angel = this.calTwoVectorsAngel(this.getNormalByTwoPoints(point2, point1), faceNormal);
      if (angel > 90) {
        thickenReverse = 0;
      }
    }
    // 根据切面方向判断加厚
    if (tangentNormal) {
      let t = new Vector3(tangentNormal.x, tangentNormal.y, tangentNormal.z).normalize();
      let f = new Vector3(faceNormal.x, faceNormal.y, faceNormal.z).normalize();
      let angel = this.calTwoVectorsAngel(t, f);
      if (outward === 0) {
        if (angel >= 90) {
          thickenReverse = 1;
        } else {
          thickenReverse = 0;
        }
      } else {
        if (angel >= 90) {
          thickenReverse = 0;
        } else {
          thickenReverse = 1;
        }
      }
    }
    return {
      thickenReverse,
      entityId
    }
  }

  /**
   * 基础切面
   * @returns {Promise<{cutFaceRet: (number|*), sPoint: null, cutFaceLargeRet: (number|string), exturdeFaces: *, error: number,
   * exturdeFaceEntityId: *, direction: ({left: number, edgeDirection: {rightPoint: *, leftPoint: *}, right: number}|
   * {left: number, edgeDirection: {rightPoint: *, leftPoint: *}, right: number})}>}
   */
  async getBaseCutFace() {
    // 1、获取元素选择信息
    let orignFace = this.selectedParam.curveFace.elements?.[0];
    let pointId = this.selectedParam.startPoint.elements?.[0].id?.split('_')?.[0];
    let pointId1 = this.selectedParam.endPoint.elements?.[0].id?.split('_')?.[0];
    let edgeIds = this.selectedParam.path.allElementIds;

    // 2、获取切出宽度和长桁高度
    let {cutWidth, outward, height, maxCutWidth} = this.getCutFaceParams();

    // 3、根据pointId获取起点坐标
    let sPoint = null;
    let ePoint = null;
    let startPointRet =
      await this.getQueryServiceByType("getElementsByIds", [parseInt(pointId)], [])
    let points = startPointRet.allElements[0]?.params?.curvePoints;
    if (points && points.length > 0) {
      sPoint = points.find(point => point.id === this.selectedParam.startPoint.elements[0].id)?.position;
    }
    let mPoint = startPointRet.allElements[0]?.mPoint;
    if (!sPoint || !mPoint) {
      throw "lineError";
    }

    // 3.1、获取终点坐标
    let endPointRet = await this.getQueryServiceByType("getElementsByIds", [parseInt(pointId1)], [])
    let endPoints = endPointRet.allElements[0]?.params.curvePoints;
    if (points && points.length > 0) {
      ePoint = endPoints.find(point => point.id === this.selectedParam.endPoint.elements[0].id)?.position;
    }
    if (!ePoint) {
      throw "lineError";
    }

    // 3.2 判断线条的有效性
    let linesRet =
      await this.getQueryServiceByType("getElementsByIds", edgeIds, [])
    let extendLines = [];
    this.getALineRow(linesRet.allElements, sPoint, extendLines, 2)
    if (linesRet.allElements.length !== extendLines.length) {
      throw "serialError";
    }
    let data = extendLines.at(-1);
    if (Math.abs(data?.end?.x - ePoint.x) > 1 || Math.abs(data?.end?.y - ePoint.y) > 1 || Math.abs(data?.end?.z - ePoint.z) > 1) {
      throw "lineError";
    }

    // 4、获取通过相切获得的拉伸曲面和切面法向
    let {
      exturdeFaces,
      tangentNormal,
      exturdeFaceNormal
    } = await this.getExtrudedFaces(pointId, orignFace.id, edgeIds, sPoint)

    // 5、毛坯截面
    let cutFaceLargeRet = await this.cutLargeSurface(edgeIds[0], exturdeFaces, maxCutWidth, orignFace)

    // 6、计算方向
    let direction = await this.getCutFaceDirection(outward, tangentNormal, sPoint,
      mPoint, cutFaceLargeRet.entityId, exturdeFaceNormal)

    // 7、真实截面
    let cutFaceRet = await this.getMiddleFace(
      {distance: cutWidth / 2, surfaceIds: exturdeFaces, outward: direction.left, pointInFace: true},
      {distance: cutWidth / 2, surfaceIds: exturdeFaces, outward: direction.right, pointInFace: true},
      {id: cutFaceLargeRet.id, entityId: cutFaceLargeRet.entityId}, sPoint)

    // 8、根据真实截面处理direction左右点
    direction = await this.changeDirectionPoint(cutFaceRet, direction)

    // 返回切面结果
    return {
      orignFace,        // 原曲面
      tangentNormal,    // 切面法向
      cutWidth,         // 实际界面宽度
      height,           // 加厚深度
      outward,          // 加厚方向
      sPoint,           // 起始点
      ePoint,           // 结束点
      cutFaceLargeRet,  // 用于脱模大曲面
      cutFaceRet,       // 曲面底部
      direction,        // 方向
      exturdeFaces      // 切面面元素id组合
    }
  }

  /**
   * 处理方向
   * @param faceEntity
   * @param direction
   * @returns {Promise<*>}
   */
  async changeDirectionPoint(faceEntity, direction) {
    let points = await this.getQueryServiceByType("getElementsByEntityId", faceEntity.entityId, [ElementType.VERTEX])
    let data = []
    points.allElements.forEach(ele => {
      let pos = new Vector3(ele.point.x, ele.point.y, ele.point.z)
      data.push({
        pId: ele.id,
        left: direction.edgeDirection.leftPoint.position.distanceTo(pos),
        right: direction.edgeDirection.rightPoint.position.distanceTo(pos),
        pos
      })
    })
    let left = data.find(j => j.left === Math.min(...data.map(j => j.left)))
    let right = data.find(j => j.right === Math.min(...data.map(j => j.right)))
    direction.edgeDirection.leftPoint = {
      pId: left.pId,
      position: left.pos,
    }
    direction.edgeDirection.rightPoint = {
      pId: right.pId,
      position: right.pos
    }
    return direction
  }

  /**
   * 根据长桁类型获取构造参数
   * 处理数值格式
   * @returns {{maxCutWidth: number, cutWidth: number, outward: number, height: number}}
   */
  getCutFaceParams() {
    let width = 0;
    let height = 0;
    let maxWidth = 0;
    switch (this.type) {
      case "typeT":
        width = parseFloat(this.sizeParam.typeT.B);
        height = parseFloat(this.sizeParam.typeT.H);
        this.sizeParam.typeT.B = parseFloat(this.sizeParam.typeT.B);
        this.sizeParam.typeT.H = parseFloat(this.sizeParam.typeT.H);
        this.sizeParam.typeT.S = parseFloat(this.sizeParam.typeT.S);
        this.sizeParam.typeT.S1 = parseFloat(this.sizeParam.typeT.S1);
        break;
      case "typeG":
        width = parseFloat(this.sizeParam.typeG.B);
        height = parseFloat(this.sizeParam.typeG.H);
        this.sizeParam.typeG.B = parseFloat(this.sizeParam.typeG.B);
        this.sizeParam.typeG.H = parseFloat(this.sizeParam.typeG.H);
        this.sizeParam.typeG.S = parseFloat(this.sizeParam.typeG.S);
        this.sizeParam.typeG.S1 = parseFloat(this.sizeParam.typeG.S1);
        this.sizeParam.typeG.S2 = parseFloat(this.sizeParam.typeG.S2);
        break;
      case "typeZ":
        width = parseFloat(this.sizeParam.typeZ.B) + parseFloat(this.sizeParam.typeZ.b) - parseFloat(this.sizeParam.typeZ.S)
        height = parseFloat(this.sizeParam.typeZ.H);
        this.sizeParam.typeZ.B = parseFloat(this.sizeParam.typeZ.B);
        this.sizeParam.typeZ.b = parseFloat(this.sizeParam.typeZ.b);
        this.sizeParam.typeZ.H = parseFloat(this.sizeParam.typeZ.H);
        this.sizeParam.typeZ.S = parseFloat(this.sizeParam.typeZ.S);
        this.sizeParam.typeZ.S1 = parseFloat(this.sizeParam.typeZ.S1);
        this.sizeParam.typeZ.S2 = parseFloat(this.sizeParam.typeZ.S2);
        break;
      case "typeC":
        if (this.sizeParam.typeC.direction === 2) {
          width = parseFloat(this.sizeParam.typeC.B);
          height = parseFloat(this.sizeParam.typeC.H) > parseFloat(this.sizeParam.typeC.h)
            ? parseFloat(this.sizeParam.typeC.H) : parseFloat(this.sizeParam.typeC.h);
        } else {
          width = parseFloat(this.sizeParam.typeC.H) > parseFloat(this.sizeParam.typeC.h)
            ? parseFloat(this.sizeParam.typeC.H) : parseFloat(this.sizeParam.typeC.h);
          height = parseFloat(this.sizeParam.typeC.B);
        }
        this.sizeParam.typeC.B = parseFloat(this.sizeParam.typeC.B);
        this.sizeParam.typeC.H = parseFloat(this.sizeParam.typeC.H);
        this.sizeParam.typeC.h = parseFloat(this.sizeParam.typeC.h);
        this.sizeParam.typeC.S = parseFloat(this.sizeParam.typeC.S);
        this.sizeParam.typeC.S1 = parseFloat(this.sizeParam.typeC.S1);
        this.sizeParam.typeC.S2 = parseFloat(this.sizeParam.typeC.S2);
        break;
      case "typeVL":
      case "typeSL":
      case "typeLL":
        width = parseFloat(this.sizeParam.typeL.B);
        height = parseFloat(this.sizeParam.typeL.H);
        this.sizeParam.typeL.B = parseFloat(this.sizeParam.typeL.B);
        this.sizeParam.typeL.H = parseFloat(this.sizeParam.typeL.H);
        this.sizeParam.typeL.S = parseFloat(this.sizeParam.typeL.S);
        this.sizeParam.typeL.S1 = parseFloat(this.sizeParam.typeL.S1);
        this.sizeParam.typeL.x = parseFloat(this.sizeParam.typeL.x);
        break;
      case "typeJ":
        width = parseFloat(this.sizeParam.typeJ.B);
        height = parseFloat(this.sizeParam.typeJ.H);
        this.sizeParam.typeJ.B = parseFloat(this.sizeParam.typeJ.B);
        this.sizeParam.typeJ.H = parseFloat(this.sizeParam.typeJ.H);
        this.sizeParam.typeJ.b = parseFloat(this.sizeParam.typeJ.b);
        this.sizeParam.typeJ.b1 = parseFloat(this.sizeParam.typeJ.b1);
        this.sizeParam.typeJ.b2 = parseFloat(this.sizeParam.typeJ.b2);
        this.sizeParam.typeJ.S = parseFloat(this.sizeParam.typeJ.S);
        break;
    }
    return {
      /**
       * 实际截面宽度
       */
      cutWidth: width,
      /**
       * 切出截面宽度 ，用于脱模
       * 除了钝角和锐角外，其它按照真是宽度加4处理
       */
      maxCutWidth: maxWidth === 0 ? width + 4 : maxWidth,
      /**
       * 长桁创建方向
       */
      outward: this.sizeParam.outward,
      /**
       * 长桁高度
       */
      height
    }
  }

  /**
   * 按照起点切面确定左右方向
   * @param outward
   * @param tNormal
   * @param sPoint
   * @param mPoint
   * @param faceEntityId
   * @param eNormal
   * @returns {Promise<{faceNormal: *, left: number, edgeDirection: {rightPoint: ({pId: *, position: Vector3|Vector3}|{pId: *, position: Vector3|Vector3}), leftPoint: ({pId: *, position: Vector3|Vector3}|{pId: *, position: Vector3|Vector3})}, isSame: boolean, right: number}>}
   */
  async getCutFaceDirection(outward, tNormal, sPoint, mPoint, faceEntityId, eNormal) {
    let distances = [];
    let elementItems = [];
    // 利用开始点，来获取贴合边，并获取两点
    let elementsRet = await this.getQueryServiceByType("getElementsByEntityId", faceEntityId, [])
    elementsRet.allElements.filter(j => j.type === ElementType.EDGE).forEach(j => {
      let start = j.params.startPoint;
      let end = j.params.endPoint;
      let distance = this.distanceToLineSegment(new Vector3(start[0], start[1], start[2]),
        new Vector3(end[0], end[1], end[2]),
        new Vector3(sPoint.x, sPoint.y, sPoint.z))
      elementItems.push({start, end, distance, id: j.id})
      distances.push(distance)
    })

    let minDistance = Math.min(...distances)
    let targetItem = elementItems.find(item => item.distance === minDistance)
    // 获取对应的点id
    let pointA = elementsRet.allElements.find(j => j.type === ElementType.VERTEX
      && ((Math.abs(j.params.position.x - targetItem.start[0]) < 0.5
        && Math.abs(j.params.position.y - targetItem.start[1]) < 0.5
        && Math.abs(j.params.position.z - targetItem.start[2]) < 0.5)))

    let pointB = elementsRet.allElements.find(j => j.type === ElementType.VERTEX
      && ((Math.abs(j.params.position.x - targetItem.end[0]) < 0.5
        && Math.abs(j.params.position.y - targetItem.end[1]) < 0.5
        && Math.abs(j.params.position.z - targetItem.end[2]) < 0.5)))
    let measureData = {
      point1: {
        position: new Vector3(pointA.params.position.x, pointA.params.position.y, pointA.params.position.z),
        pId: pointA.id
      },
      point2: {
        position: new Vector3(pointB.params.position.x, pointB.params.position.y, pointB.params.position.z),
        pId: pointB.id
      },
      lineId: targetItem.id
    }

    // 求逆切面法向的第三点
    let pointStart = new Vector3(sPoint.x, sPoint.y, sPoint.z)
    let tangentNormal = new Vector3(tNormal.x, tNormal.y, tNormal.z)
    let thirdPoint = pointStart.clone().addScaledVector(tangentNormal.normalize(), -100)

    // 三点获取面法向
    let faceNormal = this.getNormalByThreePointsAndDirection(measureData.point1.position, measureData.point2.position,
      thirdPoint, mPoint, sPoint)

    // 旋转
    let {rotatedPoint} = this.getRotatePointByAxisAndAngel(faceNormal, 90, sPoint, thirdPoint, 100, 0)

    // 计算距离
    let p1 = measureData.point1.position.distanceTo(rotatedPoint)
    let p2 = measureData.point2.position.distanceTo(rotatedPoint)

    // 根据距离进行判断 距离小的为左边
    let leftPoint = p1 > p2 ? measureData.point2 : measureData.point1;
    let rightPoint = p1 > p2 ? measureData.point1 : measureData.point2;
    let edgeDirection = {
      leftPoint, rightPoint
    };
    if (outward !== 0) {
      edgeDirection = {
        leftPoint: rightPoint,
        rightPoint: leftPoint
      }
    }

    // 根据左右点计算偏移方向
    let vecAngel = this.calTwoVectorsAngel(eNormal, {
      x: edgeDirection.rightPoint.position.x - edgeDirection.leftPoint.position.x,
      y: edgeDirection.rightPoint.position.y - edgeDirection.leftPoint.position.y,
      z: edgeDirection.rightPoint.position.z - edgeDirection.leftPoint.position.z
    })
    if (vecAngel < 90) {
      // 切面法向和左到右线点相同
      return {
        edgeDirection,
        faceNormal,
        left: 1,
        right: 0,
        isSame: true
      }
    } else {
      return {
        edgeDirection,
        faceNormal,
        left: 0,
        right: 1,
        isSame: false
      }
    }
  }

  /**
   * 切割曲面
   * @param distance
   * @param surfaceIds
   * @param reverse
   * @param faceData
   * @param targetPoint
   * @param pointInFace
   * @param needCopy
   * @returns {Promise<{entityId: *, id}|number>}
   */
  async getSplitedSurface(distance, surfaceIds, reverse, faceData, targetPoint, pointInFace, needCopy) {
    // 1、复制要被切割的曲面
    let copyFaceEntityId = faceData.entityId;
    if (needCopy) {
      let copyFaces = null;
      copyFaces = await this.copySurfaceByCount([faceData.id]);
      copyFaceEntityId = copyFaces[0].params?.entityRefId;
    }

    // 2、偏移切面（自带复制效果，需要隐藏被复制的切割面）
    let offsetRet = await this.offsetSurfaces(surfaceIds, distance, reverse);

    // 3、使用切面切割曲面
    let data = await this.splitSurface(copyFaceEntityId, offsetRet.entityId)

    // 4、选择返回的切割面，此face为贴合targetPoint的面
    let face = this.getNeedFace(data.filter(j => j.type === ElementType.FACE), targetPoint, false)
    let face1 = data.find(d => d.id === face.id && d.type === ElementType.FACE);
    let face2 = data.find(d => d.id !== face.id && d.type === ElementType.FACE);

    if (pointInFace) {
      // 5、 隐藏多余
      if (face2?.params?.entityRefId) {
        await this.getIncrementServiceByName("setEntityVisible", false, "", {
          entityIds: [face2.params.entityRefId],
          status: 0
        })
      }
      return {
        id: face1.id,
        entityId: face1.params.entityRefId
      }
    } else {
      // 5、 隐藏多余
      if (face1?.params?.entityRefId) {
        await this.getIncrementServiceByName("setEntityVisible", false, "", {
          entityIds: [face1.params.entityRefId],
          status: 0
        })
      }
      return {
        id: face2.id,
        entityId: face2.params.entityRefId
      }
    }
  }

  /**
   * 获取中间面
   * @param leftParams
   * @param rightParams
   * @param cutFace
   * @param sPoint
   * @returns {Promise<number|*>}
   */
  async getMiddleFace(leftParams, rightParams, cutFace, sPoint) {
    // 切除左边
    let face1 = await this.getSplitedSurface(leftParams.distance, leftParams.surfaceIds, leftParams.outward,
      cutFace, sPoint, leftParams.pointInFace, true)
    // 切除右边
    let face2 = await this.getSplitedSurface(rightParams.distance, rightParams.surfaceIds, rightParams.outward,
      face1, sPoint, rightParams.pointInFace, false)
    return face2;
  }

  /**
   * 根据面元素获取三角面片
   * @param faces
   */
  getTrianglesByFace(faces) {
    faces.forEach(face => {
      face.points = []
      let count = face.params.index.length / 3
      let index = face.params.index;
      let pos = face.params.position;
      for (let i = 0; i < count; i++) {
        let index1 = index[i * 3];
        let index2 = index[i * 3 + 1];
        let index3 = index[i * 3 + 2];
        let point1 = {
          x: pos[3 * index1],
          y: pos[3 * index1 + 1],
          z: pos[3 * index1 + 2]
        }
        let point2 = {
          x: pos[3 * index2],
          y: pos[3 * index2 + 1],
          z: pos[3 * index2 + 2]
        }
        let point3 = {
          x: pos[3 * index3],
          y: pos[3 * index3 + 1],
          z: pos[3 * index3 + 2]
        }
        face.points.push({point1, point2, point3})
      }
    })
  }

  // threejs相关算法
  /**
   * 计算点到空间三角形(三角面片)的距离
   * @param pointA
   * @param pointB
   * @param pointC
   * @param target
   * @returns {number}
   */
  getDistanceToTriangle(pointA, pointB, pointC, target) {
    // 定义点
    let point1 = new Vector3(pointA.x, pointA.y, pointA.z)
    let point2 = new Vector3(pointB.x, pointB.y, pointB.z)
    let point3 = new Vector3(pointC.x, pointC.y, pointC.z)
    let pointTarget = new Vector3(target.x, target.y, target.z)

    // 计算三角形的两条边向量
    let edge1 = point2.clone().sub(point1);
    let edge2 = point3.clone().sub(point1);

    // 计算目标点到三角形第一个顶点的向量
    let targetToVertex = pointTarget.clone().sub(point1);

    // 计算边向量叉积，得到三角形法向量
    let normal = new Vector3().crossVectors(edge1, edge2).normalize();

    // 计算目标点到三角形所在平面的距离
    let distanceToPlane = targetToVertex.dot(normal);

    // 计算投影点
    let projectPoint = pointTarget.clone().sub(normal.clone().multiplyScalar(distanceToPlane));

    // 检查投影点是否在三角形内部
    if (this.isPointInTriangle(point1, point2, point3, projectPoint)) {
      return Math.abs(distanceToPlane);
    } else {
      let distanceToEdge1 = this.distanceToLineSegment(point1, point2, pointTarget)
      let distanceToEdge2 = this.distanceToLineSegment(point2, point3, pointTarget)
      let distanceToEdge3 = this.distanceToLineSegment(point3, point1, pointTarget)
      // 返回最短距离
      return Math.min(distanceToEdge1, distanceToEdge2, distanceToEdge3)
    }
  }

  /**
   * 计算点的投影是否在三角形内部
   * @param point1
   * @param point2
   * @param point3
   * @param point
   * @returns {boolean}
   */
  isPointInTriangle(point1, point2, point3, point) {
    let v0 = point3.clone().sub(point1);
    let v1 = point2.clone().sub(point1);
    let v2 = point.clone().sub(point1);

    let dot00 = v0.dot(v0);
    let dot01 = v0.dot(v1);
    let dot02 = v0.dot(v2);
    let dot11 = v1.dot(v1);
    let dot12 = v1.dot(v2);

    let invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
    let u = (dot11 * dot02 - dot01 * dot12) * invDenom;
    let v = (dot00 * dot12 - dot01 * dot02) * invDenom;

    return (u >= 0) && (v >= 0) && (u + v <= 1);
  }

  /**
   * 计算点到边的距离
   * @param start
   * @param end
   * @param point
   * @returns {*}
   */
  distanceToLineSegment(start, end, point) {
    // 类型判断
    if (!(start instanceof Vector3)) {
      start = new Vector3(start.x, start.y, start.z)
    }
    if (!(end instanceof Vector3)) {
      end = new Vector3(end.x, end.y, end.z)
    }
    if (!(point instanceof Vector3)) {
      point = new Vector3(point.x, point.y, point.z)
    }
    // 距离计算
    let lineLengthSquared = start.distanceToSquared(end);
    if (lineLengthSquared === 0) {
      return point.distanceTo(start);
    }
    let t = ((point.x - start.x) * (end.x - start.x) + (point.y - start.y) * (end.y - start.y)
      + (point.z - start.z) * (end.z - start.z)) / lineLengthSquared;
    t = Math.max(0, Math.min(1, t));
    let projection = new Vector3(start.x + t * (end.x - start.x), start.y + t * (end.y - start.y),
      start.z + t * (end.z - start.z))
    return point.distanceTo(projection);
  }

  /**
   * 计算两个向量的夹角
   * @param point1
   * @param point2
   * @returns {*}
   */
  calTwoVectorsAngel(point1, point2) {
    let vec1 = new Vector3(point1.x, point1.y, point1.z)
    let vec2 = new Vector3(point2.x, point2.y, point2.z)
    let dotProduct = vec1.dot(vec2)
    let magnitude1 = vec1.length();
    let magnitude2 = vec2.length();
    let angelInRadians = Math.acos(dotProduct / (magnitude1 * magnitude2))
    // 弧度转角度后返回
    return THREE.MathUtils.radToDeg(angelInRadians);
  }

  /**
   * 通过三点和方向获取一个面法向
   * @param p1
   * @param p2
   * @param p3
   * @param startPoint
   * @param endPoint
   * @returns {*}
   */
  getNormalByThreePointsAndDirection(p1, p2, p3, startPoint, endPoint) {
    let v1 = new Vector3(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z)
    let v2 = new Vector3(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z)
    let faceNormal = new Vector3().crossVectors(v1, v2).normalize()
    let direction = new Vector3().subVectors(endPoint, startPoint).normalize();
    let angel = this.calTwoVectorsAngel(faceNormal, direction)
    if (angel > 90) {
      return faceNormal.negate();
    } else {
      return faceNormal;
    }
  }

  /**
   * 根据两点确定法向
   * @param p1
   * @param p2
   * @returns {*}
   */
  getNormalByTwoPoints(p1, p2) {
    p1 = new Vector3(p1.x, p1.y, p1.z)
    p2 = new Vector3(p2.x, p2.y, p2.z)
    return p2.clone().sub(p1).normalize();
  }

  /**
   * 点绕线段旋转
   * @param normal
   * @param angel
   * @param lineStart
   * @param lineEnd
   * @param distance
   * @param modelDirection
   * @returns {{faceMoveDirection: number, lineAndPointDistance: number, moveLength: number, rotatedPoint: *}}
   */
  getRotatePointByAxisAndAngel(normal, angel, lineStart, lineEnd, distance, modelDirection) {
    distance = distance * 1.02;
    // 1、 计算线条方向
    let lineStartPoint = new Vector3(lineStart.x, lineStart.y, lineStart.z)
    let lineEndPoint = new Vector3(lineEnd.x, lineEnd.y, lineEnd.z)
    let lineDirection = lineEndPoint.clone().sub(lineStartPoint).normalize();
    // 2、计算获取目标点
    let targetEndPoint = null;
    let targetStartPoint = null;
    let rad = 0;
    if (modelDirection === 0) {
      targetStartPoint = lineStartPoint;
      targetEndPoint = lineStartPoint.clone().addScaledVector(lineDirection, distance)
      rad = THREE.MathUtils.degToRad(angel)
    } else {
      targetStartPoint = lineEndPoint;
      targetEndPoint = lineEndPoint.clone().addScaledVector(lineDirection, distance)
      rad = THREE.MathUtils.degToRad(180 - angel)
    }
    // 3、旋转点
    // 处理目标点位移
    let targetPointToZero = new Vector3().subVectors(targetEndPoint, targetStartPoint)
    // 旋转，正数角度默认向左
    let rotationMatrix = new THREE.Matrix4().makeRotationAxis(normal, rad);
    targetPointToZero.applyMatrix4(rotationMatrix)
    // 处理目标点位移
    let rotatedPoint = targetPointToZero.add(targetStartPoint)
    // 4、计算距离
    // 切面交点-旋转点的边
    let hypotenuse = new Vector3().subVectors(rotatedPoint, lineStartPoint)
    // 计算投影长度
    let projectionLength = hypotenuse.dot(lineDirection)
    // 计算在直线最近点
    let closestPoint = new Vector3().copy(lineStartPoint).add(lineDirection.multiplyScalar(projectionLength))
    let faceMoveDirection = 0; // 0表示向右，1表示向左
    if (modelDirection === 0) {
      if (angel > 90) {
        faceMoveDirection = 1;
      }
    } else {
      // 按照投影点来确定
      if (angel < 90) {
        let leftToRightLength = lineStartPoint.distanceTo(lineEndPoint)
        let rotateToRightLength = closestPoint.distanceTo(lineEndPoint);
        if (rotateToRightLength > leftToRightLength) {
          faceMoveDirection = 1
        }
      }
    }
    // 距离起点的距离
    let moveLength = lineStartPoint.distanceTo(closestPoint)
    if (moveLength === 0) {
      moveLength = moveLength + 0.00000001
    }
    // 计算旋转点到直线的距离
    let lineAndPointDistance = new Vector3().subVectors(rotatedPoint, closestPoint).length();
    return {
      rotatedPoint,
      moveLength,
      lineAndPointDistance,
      faceMoveDirection
    }
  }

  /**
   * 获取L长桁斜边的底端
   * @param lines
   * @param startPoint
   * @param rmPoint1
   * @param rmPoint2
   * @param width
   * @returns {*[]}
   */
  getLinesByFaceAndPoint(lines, startPoint, rmPoint1, rmPoint2, width) {
    // 排除掉前后边所在边
    let tempLines = [];
    lines.forEach(line => {
      tempLines.push({
        distance1: this.distanceToLineSegment(line.sPoint, line.ePoint, rmPoint1),
        distance2: this.distanceToLineSegment(line.sPoint, line.ePoint, rmPoint2),
        line
      })
    })
    let minDistance1 = Math.min(...tempLines.map(line => line.distance1))
    let minDistance2 = Math.min(...tempLines.map(line => line.distance2))
    let removeIds = tempLines.filter(line => line.distance1 === minDistance1 || line.distance1 === minDistance2
      || line.distance2 === minDistance1 || line.distance2 === minDistance2).map(data => data.line.id)
    let existLines = lines.filter(ele => !removeIds.find(id => ele.id === id))
    let requireLines = [];
    // 梳理一组连续线段
    this.getALineRow(existLines, startPoint, requireLines, width)
    return requireLines.map(j => j.line);
  }

  /**
   * 给定线段，根据指定一点找出一组连续的线
   * @param lines
   * @param startPoint
   * @param requireLines
   * @param width
   */
  getALineRow(lines, startPoint, requireLines, width) {
    //根据lines找到完整的一侧
    let lineItems = [];
    let distances = [];
    if (!(startPoint instanceof Vector3)) {
      startPoint = new Vector3(startPoint.x, startPoint.y, startPoint.z)
    }
    lines.forEach(line => {
      let point1 = new Vector3(line.sPoint.x, line.sPoint.y, line.sPoint.z)
      let point2 = new Vector3(line.ePoint.x, line.ePoint.y, line.ePoint.z)
      let distance1 = point1.distanceTo(startPoint);
      let distance2 = point2.distanceTo(startPoint);
      distances.push(distance1);
      distances.push(distance2);
      lineItems.push({
        line,
        pointA: {
          point1, distance1
        },
        pointB: {
          point2, distance2
        },
        mPoint: line.mPoint
      })
    })
    let minDistance = Math.min(...distances)
    if (width && minDistance > (width - 1)) {
      return;
    }
    let targetLine = lineItems.find(item => item.pointA.distance1 === minDistance || item.pointB.distance2 === minDistance)
    let existLines = lines.filter(l => l.id !== targetLine.line.id)
    let newStartPoint = null;
    if (targetLine.pointA.distance1 === minDistance) {
      newStartPoint = targetLine.pointB.point2;
      targetLine.end = newStartPoint;
      targetLine.start = targetLine.pointA.point1;
    }
    if (targetLine.pointB.distance2 === minDistance) {
      newStartPoint = targetLine.pointA.point1;
      targetLine.end = newStartPoint;
      targetLine.start = targetLine.pointA.point2;
    }
    requireLines.push(targetLine)
    if (existLines?.length === 0) {
      return;
    }
    this.getALineRow(existLines, newStartPoint, requireLines, width)
  }

  /**
   * 获取对应边
   * @param lines1
   * @param lines2
   * @returns {*[]}
   */
  getDoubleLines(lines1, lines2) {
    let pair = [];
    lines1.forEach(line => {
      let targetPoint = new Vector3(line.mPoint.x, line.mPoint.y, line.mPoint.z)
      let distancePoints = [];
      lines2.forEach(l => {
        let mPoint = new Vector3(l.mPoint.x, l.mPoint.y, l.mPoint.z)
        distancePoints.push({
          l, distance: targetPoint.distanceTo(mPoint)
        })
      })
      let lineItem = distancePoints.find(p => p.distance === Math.min(...distancePoints.map(j => j.distance)))
      if (lineItem) {
        pair.push({
          lineA: line,
          lineB: lineItem.l
        })
      }
    })
    return pair
  }

  /**
   * 获取最近边
   * @param targetPoint
   * @param lines
   * @returns {*}
   */
  getNearestLineByPoint(targetPoint, lines) {
    lines.allElements.forEach(line => {
      line.distance = this.distanceToLineSegment(line.sPoint, line.ePoint, targetPoint)
    })
    return lines.allElements.find(l => l.distance === Math.min(...lines.allElements.map(line => line.distance)))
  }
}

const trussDesignCommand = new TrussDesignCommand()
export default trussDesignCommand